package com.dingding.common.core.encrpt;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.ArrayUtils;

import com.dingding.common.core.config.SystemKeyConfig;

/**
 * 
 * RSA安全编码
 * @version 1.6
 * @since 1.6
 * @author chehuanbo
 * 
 */
public abstract class RSACoder extends Coder {

	private static final String KEY_ALGORITHM = "RSA"; // 加密方式
	private static final String PUBLIC_KEY = "RSAPublicKey"; // 公钥KEY
	private static final String PRIVATE_KEY = "RSAPrivateKey";// 私钥KEY

	/**
	 * 初始化密钥 （生成秘钥）
	 * @return Map 初始化的秘钥对
	 * @throws Exception
	 * @author chehuanbo
	 */
	public static Map<String, Object> initKey() throws Exception {

		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(1024);

		KeyPair keyPair = keyPairGen.generateKeyPair();

		// 公钥
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

		// 私钥
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

		Map<String, Object> keyMap = new HashMap<String, Object>(2);

		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);

		return keyMap;

	}

	/**
	 * 解密<br>
	 * 用私钥解密
	 * 解密流程：
	 * 1：Base64解码
	 * 2：分段解密
	 * 3：URLDecoder编码
	 * @param requestEncrptData
	 *            客户端请求过来的加密后的数据
	 * @return String 解密后的数据
	 * @throws Exception
	 * @author chehuanbo
	 * @since V1.6
	 * 
	 */
	public static String decryptByPrivateKey(String requestEncrptData) throws Exception {

		// 对密钥Base64解码
		byte[] keyBytes = decryptBASE64(SystemKeyConfig.getPrivateKey());
		// 取得私钥
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

		// 对数据解密
		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.DECRYPT_MODE, privateKey);

		// base64解码
		Base64 base64 = new Base64();
		byte[] byteData = base64.decode(requestEncrptData);

		// 获取数据长度
		int byteDataLength = byteData.length;
		// 分段解密，每次读取的最大长度为128
		StringBuilder stringbuffer = new StringBuilder();
		for (int i = 0; i < byteDataLength; i += 128) {
			byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(byteData, i, i + 128));
			stringbuffer.append(new String(doFinal));
		}

		// 由于可能存在中文乱码的情况，所以需要decode编码
		String decryptStr = URLDecoder.decode(stringbuffer.toString(), "UTF-8");

		return decryptStr;

	}

	/**
	 * 用公钥加密
	 * 加密流程
	 * 1：URLEncoder编码
	 * 2：执行分段加密（RSA对内容长度有限制）
	 * 3：Base64转换
	 * @param responseDataJson
	 *            响应给客户端待加密的数据
	 * @return String 响应给客户端加密后的数据
	 * @throws Exception
	 * @author chehuanbo
	 * @since V1.6
	 * 
	 */
	public static String encryptByPublicKey(String responseDataJson) throws Exception {

		// 对公钥Base64解密
		byte[] keyBytes = decryptBASE64(SystemKeyConfig.getPublicKey());
		// 取得公钥
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicKey = keyFactory.generatePublic(x509KeySpec);

		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);

		// 由于可能存在中文乱码的情况，所以需要decode编码
		String encryptStr = URLEncoder.encode(responseDataJson, "UTF-8");
		byte[] byteData = encryptStr.getBytes();
		// 获取数据长度
		int byteDataLength = byteData.length;
		// 分段加密
		byte[] dataReturn = null;
		for (int i = 0; i < byteDataLength; i += 100) {
			byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(byteData, i, i + 100));
			dataReturn = ArrayUtils.addAll(dataReturn, doFinal);
		}

		// 转换成Base64字符串
		Base64 base64 = new Base64();
		String resEncrptData = base64.encodeToString(dataReturn);

		return resEncrptData;

	}

	/**
	 * 取得私钥
	 * 
	 * @param keyMap 秘钥对集合
	 * @return String 获取到的私钥
	 * @throws Exception
	 * @author chehuanbo
	 * @since V1.6
	 * 
	 */
	public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PRIVATE_KEY);
		return encryptBASE64(key.getEncoded());
	}

	/**
	 * 取得公钥
	 * 
	 * @param keyMap 秘钥对集合
	 * @return String 获取到的公钥
	 * @throws Exception
	 * @author chehuanbo
	 * @since V1.6
	 * 
	 */
	public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PUBLIC_KEY);
		return encryptBASE64(key.getEncoded());
	}

}
